# -*- coding: utf-8 -*-
from openerp.osv import fields
from openerp.osv import osv
from openerp import tools, api
from datetime import date
from openerp.tools import float_is_zero
import datetime
from openerp.addons.stock_account.wizard.stock_invoice_onshipping import JOURNAL_TYPE_MAP
from openerp import workflow
from openerp import models, SUPERUSER_ID


class purchase_line_invoice(osv.osv_memory):
    _inherit = 'purchase.order.line_invoice'
    # 供应商发票字段扩展

    def makeInvoices(self, cr, uid, ids, context=None):
        returns = super(purchase_line_invoice, self).makeInvoices(cr, uid, ids, context=context)
        domain = returns.get('domain')
        account_invoice_ids = self.pool.get('account.invoice').search(cr, uid, eval(domain), context=context)
        for account_invoice in self.pool.get('account.invoice').browse(cr, uid, account_invoice_ids, context=context):
            account_invoice.write({'official_invoice_line':  [(0, False, {'invoice_title': (self.pool.get('res.users').browse(cr, uid, uid, context=context)).company_id.name,
                                                                          'invoice_money':   account_invoice.amount_total,
                                                                          'invoice_corp': account_invoice.partner_id.name})]})
        return returns


class account_move(osv.osv):
    _inherit = 'account.move'
    _columns = {
        'line_name': fields.related('line_id', 'name', type='char', string=u"明细名称", readonly=True)
    }

    def unlink(self, cr, uid, ids, context=None):
        if len(ids)==1:
            move = self.browse(cr, uid, ids, context=context)
            expense_obj = self.pool.get('hr.expense.expense')
            expense_id = expense_obj.search(cr, uid,[('account_move_id', '=', move.id)], context=context)
            if expense_id:
                if isinstance(expense_id, int):
                    expense_id = [expense_id]
                if expense_obj.search(cr, uid, [('account_move_id', '=', move.id),
                                                ('state', '=', 'paid')], context=context):
                    expense_obj.write(cr, uid, expense_id, {'state': 'done', 'account_move_id': False}, context=context)
                expense_obj.signal_workflow(cr, uid, expense_id, 'refuse')
                expense_obj.signal_workflow(cr, uid, expense_id, 'draft')
            else:
                return super(account_move, self).unlink(cr, uid, ids, context=context)
        else:
            return super(account_move, self).unlink(cr, uid, ids, context=context)


class account_move_reconcile(osv.osv):
    _inherit = 'account.move.reconcile'

    def unlink(self, cr, uid, ids, context=None):
        sale_ids =[]
        for reconcile_row in self.browse(cr, uid, ids, context=context):
            move_ids = [line.move_id.id for line in reconcile_row.line_id if line.move_id]
            invoice_ids = self.pool.get('account.invoice').search(cr, uid, [('move_id', 'in', move_ids)])
            if invoice_ids:
                cr.execute("SELECT order_id FROM "
                           "sale_order_invoice_rel WHERE  invoice_id in %s" %
                           (str(invoice_ids).replace('[', '(').replace(']', ')')))
                sale_ids = cr.fetchall()
                if sale_ids:
                    self.pool.get('sale.order').signal_workflow(cr, uid,
                                                                [sale_id[0] for sale_id in sale_ids],
                                                                'unlink_reconcile')
        return_vals = super(account_move_reconcile, self).unlink(cr, uid, ids, context=context)
        return return_vals
    
    def create(self, cr, uid, vals, context=None):
        return_id = super(account_move_reconcile, self).create(cr, uid, vals, context=context)
        reconcile_row = self.browse(cr, uid, return_id, context=context)
        move_ids = [line.move_id.id for line in reconcile_row.line_id if line.move_id]
        invoice_ids = self.pool.get('account.invoice').search(cr, uid, [('move_id', 'in', move_ids)])
        if invoice_ids:
            self.pool.get('account.invoice').browse(cr, uid, invoice_ids, context=context)
        return return_id
        
class account_invoice(osv.osv):
    _inherit = 'account.invoice'
    _description = u"发票"

    def reverse_write_off(self, cr, uid, ids, context=None):
        reconcile_obj = self.pool.get('account.move.reconcile')
        for invoice in self.browse(cr, uid, ids, context=context):
            for line in invoice.move_id.line_id:
                if line.reconcile_ref:
                    reconcile_id = reconcile_obj.search(cr, uid, [('name', '=', line.reconcile_ref)], context=context)
                    reconcile_obj.unlink(cr, uid, reconcile_id, context=context)
            sale_id = self.pool.get('sale.order').search(cr, uid, [('name', '=', invoice.origin)])
            if sale_id:
                invoice.signal_workflow('invoice_cancel')
                self.pool.get('sale.order').write(cr, uid, sale_id, {'state': 'progress'}, context=context)
                self.pool.get('sale.order').signal_workflow(cr, uid, sale_id, 'dftg_invoice_except', context=context)
                invoice.unlink()
        return True

    def _compute_official_invoice_sum(self, cr, uid, ids, name, arg, context=None):
        res = {}
        for account_invoice in self.browse(cr, uid, ids, context=context):
            amount_total = 0
            for official_invoice_line in account_invoice.official_invoice_line:
                amount_total = amount_total + official_invoice_line.invoice_money
            is_enough = amount_total >= account_invoice.amount_total or False
            res[account_invoice.id] = {'official_invoice_sum': amount_total,
                                       'official_invoice_is_enough': is_enough}
        return res
    # 暂时去除  纸质发票金额必须和系统发票金额一致！ 检查
    def write(self, cr, uid, ids, vals, context=None):
        return_vals = super(account_invoice, self).write(cr, uid, ids, vals, context=context)
        for invoice in self.browse(cr, uid, ids, context=context):
            self.card_order_invocie_deal(cr, uid, invoice.origin,
                                                      [line.id for line in invoice.invoice_line],
                                                      context=context)
            sum_official_invoice_money = sum([round(invoice_line.invoice_money, 2)
                                              for invoice_line in invoice.official_invoice_line])
            if invoice.official_invoice_line and abs(sum_official_invoice_money - invoice.amount_total) > 0.001:
                if len(invoice.official_invoice_line) == 1 and not invoice.official_invoice_line[0].is_invoiced:
                    # 纸质发票申请和系统发票必须保持一致所以要修改对应的纸质发票 2（前提是纸质发票申请只有一条记录， 并且是没有收到 发票的）
                    invoice.official_invoice_line[0].write({'invoice_money': invoice.amount_total})
                else:
                    raise osv.except_osv(u'错误', '纸质发票金额必须和系统发票金额一致！系统发票总金额%s,\
                    \n目前生成的纸质发票申请金额%s'% (invoice.amount_total, sum_official_invoice_money))
            if invoice.period_id and vals.get('period_id') and invoice.period_id.special:
                raise osv.except_osv(u'错误', u'不能修改为已关闭的会计期间!')
            if vals.get('period_id'):
                if invoice.move_id:
                    cr.execute("""UPDATE account_move SET period_id=%s WHERE id=%s""" % (invoice.period_id.id,
                                                                                         invoice.move_id.id))
                    cr.execute("""UPDATE account_move_line SET period_id=%s WHERE move_id =%s""" % (invoice.period_id.id,
                                                                                                     invoice.move_id.id))
        return return_vals
    #vals.get('invoice_line', False) and isinstance(vals.get('invoice_line'), list) and \
    #                     len(vals.get('invoice_line')) > 0 and isinstance(vals.get('invoice_line')[0], tuple) \
    #             and len(vals.get('invoice_line')[0]) == 3
    # #
    def card_order_invocie_deal(self, cr, uid, origin, line_ids, context=None):
        return_val = {}
        order_obj = self.pool.get('sale.order')
        line_obj = self.pool.get('account.invoice.line')
        account_account_id = self.pool.get('account.account').search(cr, uid,
                                                                     [('code', '=', '2205')], context=context)
        card_order_id = order_obj.search(cr, uid, [('name', '=', origin), ('is_card', '=', True)], context=context)
        if line_ids  and card_order_id:
            model_data_obj = self.pool.get('ir.model.data')
            card_product_id = model_data_obj.xmlid_to_res_id(
                cr, SUPERUSER_ID, 'dftg_ext.dftg_product_product_card_service_ext')
            card_product_row = self.pool.get('product.product').browse(cr, uid, card_product_id, context=context)
            if card_product_row.property_account_income.code == '2801' or \
                            card_product_row.categ_id.property_account_income_categ.code == '2801':

                if account_account_id:
                    account_account_id = isinstance(account_account_id, list) and account_account_id[
                        0] or account_account_id
                    for invoice_line in line_obj.browse(cr, uid, line_ids, context=context):
                        if invoice_line.product_id.id == card_product_id:
                            return_val.update({'account_id': account_account_id})
        shop_order_id = order_obj.search(cr, uid, [('name', '=', origin), ('is_shop_order', '=', True)], context=context)
        if line_ids and shop_order_id:
            for invoice_line in line_obj.browse(cr, uid, line_ids, context=context):
                if invoice_line.product_id.categ_id.property_account_income_categ.code != '2801':
                    raise osv.except_osv(u'警告', u'商城订单的产品分类的收入科目需设置为长期应付款!')
            return_val.update({'account_id': account_account_id and account_account_id[0]})
        return return_val

    def create(self, cr, uid, vals, context=None):
        if vals.get('origin'):
            vals.update(self.card_order_invocie_deal(cr, uid, vals.get('origin'), vals.get('invoice_line') and \
                                                     vals.get('invoice_line')[0] and len(vals.get('invoice_line')[0]) \
                                                     == 3 and vals.get('invoice_line')[0][2], context=context))
        return_val = super(account_invoice, self).create(cr, uid, vals, context=context)
        return return_val

    def _compute_invoice_money(self, cr, uid, ids, context=None):
        res = {}
        for invoice_line in self.pool.get('official.invoice.lines').browse(cr, uid, ids, context=context):
            res[invoice_line.system_invoice_id.id] = True
        return res

        # 计算发票结转余额(剩余的欠款）当是负数（退款）的时候计算方向修正
    # 计算发票结转余额(剩余的欠款）当是负数（退款）的时候计算方向修正
    def _compute_residual(self):
        for invoice_row in self:
            amount_total = invoice_row.amount_total
            invoice_row.residual = 0.0
            # Each partial reconciliation is considered only once for each invoice it appears into,
            # and its residual amount is divided by this number of invoices
            partial_reconciliations_done, all_partial_invoices = [], []
            line_amount_all = 0
            partial_reconciliation_invoices = set()
            for line in invoice_row.sudo().move_id.line_id:
                if line.account_id.type not in ('receivable', 'payable'):
                    continue
                if line.reconcile_partial_id and line.reconcile_partial_id.id in partial_reconciliations_done:
                    continue
                # Get the correct line residual amount
                if line.currency_id == invoice_row.currency_id:
                    line_amount = line.amount_residual_currency if line.currency_id else line.amount_residual
                else:
                    from_currency = line.company_id.currency_id.with_context(date=line.date)
                    line_amount = from_currency.compute(line.amount_residual, invoice_row.currency_id)
                # For partially reconciled lines, split the residual amount
                if line.reconcile_partial_id:
                    for pline in line.reconcile_partial_id.line_partial_ids:
                        if pline.invoice and invoice_row.type == pline.invoice.type:
                            partial_reconciliation_invoices.update([pline.invoice])
                            all_partial_invoices.append(pline.invoice)
                    partial_reconciliations_done.append(line.reconcile_partial_id.id)
                line_amount_all += line_amount
            all_partial_invoices = list(set(all_partial_invoices))
            if sum([invoice.amount_total for invoice in all_partial_invoices]) == - line_amount_all:
                line_amount_all = -line_amount_all
            all_partial_invoices.sort(key=lambda line: line.amount_total, reverse=True)
            # #IJZGO 当客户的销售订单几个一起核销时，均摊的算法从销售订单上不太好理解，比如截图里的第一个订单的已核销金额大大超过了收款的金额
            for invoice in all_partial_invoices:
                if line_amount_all - invoice.amount_total > 0:
                    if invoice.id == invoice_row.id:
                        line_amount_all = invoice.amount_total
                        break
                    else:
                        line_amount_all = line_amount_all - invoice.amount_total
                elif line_amount_all - invoice.amount_total < 0:
                    if invoice.id == invoice_row.id:
                        break
                    else:
                        line_amount_all = 0
                        break
                else:
                    if invoice.id == invoice_row.id:
                        line_amount_all = abs(invoice.amount_total)
                        break
                    else:
                        line_amount_all = 0
                        break

            invoice_row.residual += line_amount_all

    _columns = {
        'voucher_id': fields.many2one('account.voucher', string=u'手工凭证ID'),
        'official_invoice_line': fields.one2many('official.invoice.lines', 'system_invoice_id', u'纸质发票'),
        'official_invoice_sum': fields.function(_compute_official_invoice_sum, type="float", string=u'纸质发票总金额',
                                                store={'account.invoice': (lambda self, cr, uid, ids, c={}: ids, ['official_invoice_line', 'invoice_money'], 10),
                                                       'official.invoice.lines': (_compute_invoice_money, ['invoice_money'], 10)}, multi='invoice_official_invoice'),
        'invoice_title': fields.related('official_invoice_line', 'invoice_title', type='char', string="发票台头", readonly=True),
        'invoice_money': fields.related('official_invoice_line', 'invoice_money', type='float', string="开票金额", readonly=True),
        'invoice_corp': fields.related('official_invoice_line', 'invoice_corp', type='char', string="开票公司", readonly=True),
        'reconcile_account_name': fields.char(string="核销分录名称"),
        'reconcile_time': fields.date(string=u"核销时间"),
        'official_invoice_is_enough': fields.function(_compute_official_invoice_sum, type="boolean", string=u'纸质发票金额是否充足',
                                                      store={'account.invoice': (lambda self, cr, uid, ids, c={}: ids, ['official_invoice_line', 'invoice_money'], 10),
                                                             'official.invoice.lines': (_compute_invoice_money, ['invoice_money'], 10)}, multi='invoice_official_invoice'),
    }
    _defaults = {
        'official_invoice_is_enough': True,
    }

    # 删除发票的时候，检查是否有关联的出入库单，有的话清除
    @api.multi
    def unlink(self):
        stock_picking_obj = self.env["stock.picking"]
        for invoice in self:
            stock_picking = stock_picking_obj.search([('invoice_id', '=', invoice.id)])
            if stock_picking:
                for picking in stock_picking:
                    self._cr.execute("UPDATE stock_move SET invoice_state=%s WHERE picking_id=%s", ('2binvoiced', picking.id))
                    self._cr.execute("UPDATE stock_picking SET invoice_state=%s WHERE id=%s", ('2binvoiced', picking.id))
        return super(account_invoice, self).unlink()

    @api.multi
    def action_cancel_draft(self):
        # go from canceled state to draft state
        for invoice in self:
            if invoice.invoice_line:
                purchase_order_lines = self.env["purchase.order.line"].browse([invoice_line.purchase_line_id and invoice_line.purchase_line_id.id
                                                                              for invoice_line in invoice.invoice_line
                                                                              if invoice_line.purchase_line_id])
                all_invoiced = [purchase_order_line.invoiced for purchase_order_line in purchase_order_lines]
                if any(all_invoiced):
                    raise osv.except_osv(u'警告', u'对应的订单的发票已经存在,不能把已经取消的发票设为草稿!')
                else:
                    #把关联的采购单行的invoiced改成True
                    self._cr.execute("UPDATE purchase_order_line SET invoiced=TRUE WHERE order_id IN ("
                                "SELECT po.id FROM purchase_invoice_rel pir "
                               " INNER JOIN account_invoice ai ON ai.id=pir.invoice_id "
                               " INNER JOIN purchase_order po ON po.id=pir.purchase_id"
                               " WHERE pir.invoice_id=%s)", (invoice.id,))
        return super(account_invoice, self).action_cancel_draft()

    # 继承action_cancel方法，当取消发票时清除字段internal_number值，可以正常删除发票
    @api.multi
    def action_cancel(self):
        return_val = super(account_invoice, self).action_cancel()
        self.write({'internal_number': ''})
        stock_picking_obj = self.env['stock.picking']
        for invoice in self:
            stock_picking = stock_picking_obj.search([('invoice_id', '=', invoice.id)])
            if stock_picking:
                for picking in stock_picking:
                    self._cr.execute("UPDATE stock_move SET invoice_state=%s WHERE picking_id=%s",
                                     ('2binvoiced', picking.id))
                    self._cr.execute("UPDATE stock_picking SET invoice_state=%s WHERE id=%s", ('2binvoiced', picking.id))
            if invoice.invoice_line:
                purchase_order_line = self.env["purchase.order.line"].browse([invoice_line.purchase_line_id and invoice_line.purchase_line_id.id
                                                                              for invoice_line in invoice.invoice_line
                                                                              if invoice_line.purchase_line_id])
                purchase_order_line.write({'invoiced': False})
        ids = list(self._ids) if isinstance(self._ids, tuple) else [self._ids]
        self._cr.execute("SELECT order_id FROM "
                   "sale_order_invoice_rel WHERE  invoice_id in %s" %
                   (str(ids).replace('[', '(').replace(']', ')')))
        sale_ids = self._cr.fetchall()
        if sale_ids:
            self.pool['sale.order'].write(self._cr, self._uid, sale_ids[0], {'state': 'invoice_except'}, context=self._context)
        return return_val

# 修改“手工凭证”


class account_voucher(osv.osv):
    _inherit = 'account.voucher'
    _description = u"手工凭证"

    _columns = {
        'is_apply': fields.boolean(string=u'采购申请付款生成'),
        'invoice_ids': fields.one2many('account.invoice', 'voucher_id', string=u'对应发票'),
        'payment_company': fields.char(u'付款公司'),
    }

    _defaults = {
        'is_apply': False,
        'is_receive_official_invoice': False,
    }

    def name_get(self, cr, uid, ids, context=None):
        # 继承account voucher的name_get，当记账(编号存在)返回编号；未记账(编号不存在)返回凭证id
        res = super(account_voucher, self).name_get(cr, uid, ids, context=context)
        for voucher in self.browse(cr, uid, ids, context=context):
            if voucher.number:
                return res
            else:
                return [(voucher.id, voucher.id)]

    # 继承account_voucher的write方法，把凭证id写入到对应的发票voucher_id字段
    def write(self, cr, uid, ids, vals, context=None):
        super(account_voucher, self).write(cr, uid, ids, vals, context=context)
        voucher_line_obj = self.pool.get('account.voucher.line')
        account_invoice_obj = self.pool.get('account.invoice')
        # 查找当前凭证对应的凭证行并遍历，获得凭证行对应的发票(invoice_id)
        if isinstance(ids, int):
            ids = [ids]
        voucher_line_ids = voucher_line_obj.search(cr, uid, [('voucher_id', 'in', ids)], context=context)
        for voucher_lines in voucher_line_obj.browse(cr, uid, voucher_line_ids, context):
            # 根据发票行，找到对应发票并给voucher_id字段赋值
            account_invoice_id = voucher_lines.invoice_id.id
            if account_invoice_id:
                account_invoice_obj.write(cr, uid, account_invoice_id, {'voucher_id': ids[0]}, context=context)

        return ids

    def create(self, cr, uid, vals, context=None):
        # 重载account.voucher的create方法，对当前生成的凭证行的invoice_id字段赋值
        voucher_line_obj = self.pool.get("account.voucher.line")
        account_voucher_id = super(account_voucher, self).create(cr, uid, vals, context=context)
        voucher_line_id = voucher_line_obj.search(cr, uid, [('voucher_id', '=', account_voucher_id)], context=context)

        if voucher_line_id:
            voucher_line_obj.write(cr, uid, [max(voucher_line_id)], {'invoice_id': context.get('invoice_id')}, context=context)
        return account_voucher_id

    # 对invoice_id字段进行赋值
    def _evaluate_invoice_id(self, cr, uid, ids, res, context=None):
        move_line_pool = self.pool.get('account.move.line')
        value = res.get('value')
        # 如果res存在value,并且存在借方明细，根据line_dr_ids的move_line_id，对invoice_id赋值
        if value and value.get('line_dr_ids'):
            for dr_ids in res['value']['line_dr_ids']:
                # 判断dr_ids是否是dict类型,避免tuple类型
                if isinstance(dr_ids, dict):
                    if dr_ids.get('move_line_id'):
                        move_line = move_line_pool.browse(cr, uid, dr_ids['move_line_id'], context=context)
                        dr_ids['invoice_id'] = move_line.invoice.id

        # 如果res存在value,并且存在贷方明细，根据line_cr_ids的move_line_id，对invoice_id赋值
        if value and value.get('line_cr_ids'):
            for cr_ids in res['value']['line_cr_ids']:
                # 判断cr_ids是否是dict类型,避免tuple类型
                if isinstance(cr_ids, dict):
                    if cr_ids.get('move_line_id'):
                        move_line = move_line_pool.browse(cr, uid, cr_ids['move_line_id'], context=context)
                        cr_ids['invoice_id'] = move_line.invoice.id

        return True

    # 计算“总计”字段值
    def _evaluate_amount(self, cr, uid, ids, res, partner_id, context=None):
        purchase_order_obj = self.pool.get("purchase.order")
        value = res.get('value')
        # 获取当前供应商对应的采购单
        purchase_order = purchase_order_obj.search(cr, uid, [('partner_id', '=', partner_id)], context=context)
        voucher_ids = []
        advance_payment = 0
        # 对所有采购单上的预付款求和，并与原始amount字段求和，最终值赋给amount("总计")
        for purchase in purchase_order_obj.browse(cr, uid, purchase_order, context=context):
            for voucher in self.browse(cr, uid, purchase.voucher_id.id, context=context):
                # 排除多个采购单对应一个预付款凭证的情况
                if voucher.id in voucher_ids:
                    continue
                voucher_ids.append(voucher.id)
                advance_payment += voucher.amount
        if value:
            value['amount'] = advance_payment

        return True

    # 重载onchange_partner_id
    def onchange_partner_id(self, cr, uid, ids, partner_id, journal_id, amount, currency_id, ttype, date, context=None):
        if not ids or self.search(cr, uid, ['&', ('id', 'in', ids), ('is_apply', '=', False)], context=context):
            res = super(account_voucher, self).onchange_partner_id(cr, uid, ids, partner_id, journal_id, amount, currency_id, ttype, date, context=context)
            self._evaluate_invoice_id(cr, uid, ids, res, context=context)
#             self._evaluate_amount(cr, uid, ids, res, partner_id, context=context)
        return res

    # 重载onchange_amount方法
    def onchange_amount(self, cr, uid, ids, amount, rate, partner_id, journal_id, currency_id, ttype, date, payment_rate_currency_id, company_id, context=None):
        if not ids or self.search(cr, uid, ['&', ('id', 'in', ids), ('is_apply', '=', False)], context=context):
            default = super(account_voucher, self).onchange_amount(cr, uid, ids, amount, rate, partner_id, journal_id, currency_id, ttype, date, payment_rate_currency_id, company_id, context=context)
            self._evaluate_invoice_id(cr, uid, ids, default, context=context)
        return default

    # 重载onchange_journal
    def onchange_journal(self, cr, uid, ids, journal_id, line_ids, tax_id, partner_id, date, amount, ttype, company_id, context=None):
        res = {}
        if not ids or self.search(cr, uid, ['&', ('id', 'in', ids), ('is_apply', '=', False)], context=context):
            res = super(account_voucher, self).onchange_journal(cr, uid, ids, journal_id, line_ids, tax_id, partner_id, date, amount, ttype, company_id, context=context)
        return res

    # “记账按钮”，在记账之前先判断是否指定了付款方式
    def button_validate(self, cr, uid, ids, context=None):
        for voucher in self.browse(cr, uid, ids, context=context):
            if voucher.journal_id.code == 'CGXN':
                raise osv.except_osv(u'警告', u'还未指定付款方式，请修改付款方式之后才进行“记账”操作！')
        return self.signal_workflow(cr, uid, ids, 'proforma_voucher')
    #
    # # 继承“记账”按钮，在记账前判断是否指定了付款方式，未指定则给出提示

    def proforma_voucher(self, cr, uid, ids, context=None):
        if context is None:
            context = {}
        for voucher in self.browse(cr, uid, ids, context=context):
            if voucher.journal_id.code == 'CGXN':
                raise osv.except_osv(u'警告', u'还未指定付款方式，请修改界面上的付款方式后再进行“记账”操作！')
        super(account_voucher, self).proforma_voucher(cr, uid, ids, context=context)

# 添加“invoice_id（对应发票id）”字段


class account_voucher_line(osv.osv):
    _inherit = 'account.voucher.line'

    _columns = {
        'invoice_id': fields.many2one('account.invoice', 'voucher_id', u'对应发票ID'),
        'note': fields.text(u'备注'),
        'is_checked': fields.boolean(u'是否核查'),
    }


class account_invoice_line(models.Model):
    _inherit = 'account.invoice.line'

    @api.multi
    def unlink(self):
        for invoice_line in self:
            if invoice_line.purchase_line_id:
                purchase_order_line = self.env["purchase.order.line"].browse(invoice_line.purchase_line_id.id)
                purchase_order_line.write({'invoiced': False})
            # 判断除了要删除的行之外还有没有， 和要删除行在同一个采购单里面的行在这个发票里面
            purchase_order_row = invoice_line.purchase_line_id.order_id
            invoice_row = invoice_line.invoice_id
            order_line_ids = [line.id for line in purchase_order_row.order_line if
                              invoice_line.purchase_line_id.id != line.id]
            invoice_order_ids = [invoice_line.purchase_line_id.id for invoice_line in invoice_row.invoice_line]
            if not list(set(invoice_order_ids).intersection(set(order_line_ids))):
                purchase_order_row.write({'invoice_ids': [(3, invoice_row.id)]})
        return super(account_invoice_line, self).unlink()

    def write(self, cr, uid, ids, vals, context=None):
        # 采购发票行 产品单价变化,对应的采购单的价格也变化.
        for invoice_line_row in self.browse(cr, uid, ids, context=context):
            if vals.get('price_unit') and invoice_line_row.purchase_line_id and \
                    invoice_line_row.purchase_line_id.price_unit != vals.get('price_unit'):
                invoice_line_row.purchase_line_id.write({'price_unit': vals.get('price_unit')})
        return_vals = super(account_invoice_line, self).write(cr, uid, ids, vals, context=context)
        return return_vals


# 采购人员"申请付款"操作
class apply_payment(osv.osv):
    _name = 'apply.payment'
    _description = u"申请付款操作"
    _auto = False

    def _invoice_state_selection(self, cr, uid, ids, context=None):
        return [
            ('draft', u'草稿'),
            ('proforma2', u'形式发票'),
            ('open', u'打开'),
            ('paid', u'已付'),
            ('cancel', u'取消')]

    _columns = {
        'po_id': fields.many2one('purchase.order', string=u'采购单'),
        'partner_id': fields.many2one('res.partner', string=u'供应商'),
        'invoice_id': fields.many2one('account.invoice', string=u'发票'),
        'date_invoice': fields.date(string=u'发票日期'),
        'origin': fields.char(string=u'单据号'),
        'invoice_amount': fields.float(string=u'发票金额'),
        'invoice_state': fields.selection('_invoice_state_selection', string=u'发票状态'),
    }

    def init(self, cr):
        tools.drop_view_if_exists(cr, self._table)
        cr.execute("""CREATE or REPLACE VIEW %s as (
                      SELECT ai.id AS id,
                             po.id AS po_id,
                             po.partner_id AS partner_id,
                             ai.id AS invoice_id,
                             ai.origin AS origin,
                             ai.date_invoice AS date_invoice,
                             ai.amount_total AS invoice_amount,
                             ai.state AS invoice_state
                      FROM purchase_order AS po
                      JOIN purchase_invoice_rel AS pir ON po.id=pir.purchase_id
                      JOIN account_invoice AS ai ON pir.invoice_id=ai.id
                      WHERE ai.voucher_id IS NULL
            )
            """ % (self._table,))


class apply_payment_wizard(osv.osv_memory):
    _name = 'apply.payment.wizard'
    _description = u'申请付款向导'

    # 确认按钮
    def confirm_button(self, cr, uid, ids, context=None):
        account_invoice_obj = self.pool.get('account.invoice')
        purchase_order_obj = self.pool.get('purchase.order')

        # 查找“未指定付款方式”，若没有则新建一个
        journal_ids = self.pool.get('account.journal').search(cr, uid, [('code', '=', 'CGXN')], context=context)
        if not journal_ids:
            journal_id = self.pool.get('account.journal').create(cr, uid, {'code': 'CGXN', 'name': u'采购虚拟账户', 'type': 'bank'}, context=context)
        else:
            journal_id = journal_ids[0]

        for wizard in self.browse(cr, uid, ids, context=context):
            for line in wizard.wizard_line:
                # 计算关联的采购单的单据
                po_ids = eval(line.po_ids)
                po_codes = ''
                for po in purchase_order_obj.browse(cr, uid, po_ids, context=context):
                    if po_codes:
                        po_codes += ','
                    po_codes += po.name

                # 计算出对应的发票借贷方invoice_id和采购单po_id对应的voucher_id
                dr_invoice_list = filter(lambda x: x != '', line.dr_invoice_ids.split(','))
                cr_invoice_list = filter(lambda x: x != '', line.cr_invoice_ids.split(','))

                voucher_list = []
                for po in purchase_order_obj.browse(cr, uid, po_ids, context=context):
                    if po.voucher_id:
                        voucher_list.append(po.voucher_id.id)
                voucher_list = list(set(voucher_list))

                # 生成手工凭证记录、借方DR的手工凭证行明细、贷方CR的手工凭证行明细
                voucher_id = self.compute_account_voucher(cr, uid, po_codes, journal_id, line, context=context)
#                 voucher_dr_line_ids = self.compute_voucher_dr_line(cr, uid, voucher_id, dr_invoice_list, line, context=context)
#                 voucher_cr_line_ids = self.compute_voucher_cr_line(cr, uid, voucher_id, cr_invoice_ids, context=context)

                # 根据借贷方不同，分别生成明细
                if dr_invoice_list:
                    voucher_line_ids = self.compute_voucher_line(cr, uid, voucher_id, dr_invoice_list, line, 'dr', context=context)
                if cr_invoice_list:
                    voucher_line_ids = self.compute_voucher_line(cr, uid, voucher_id, cr_invoice_list, line, 'cr', context=context)
                voucher_payment_line_ids = self.compute_voucher_payment_line(cr, uid, voucher_id, voucher_list, context=context)

                # 根据手工凭证明细行来判断pre_line的值
                if len(voucher_payment_line_ids) or len(voucher_line_ids):
                    self.pool.get('account.voucher').write(cr, uid, voucher_id, {'pre_line': True}, context=context)
                # 将手工凭证ID写回发票，作为已经处理的标记
                account_invoice_obj.write(cr, uid, eval('[' + ','.join(dr_invoice_list) + ']'), {'voucher_id': voucher_id}, context=context)
#                 #将手工凭证ID写回采购单，作为预付款已经处理的标记
#                 if line.is_advance_payment:
#                     cr.execute("""UPDATE purchase_order
#                                   SET voucher_id= %d
#                                   WHERE id in %s AND advance_payment IS NOT NULL AND voucher_id IS NULL
#                                 """% (voucher_id, str(tuple(po_ids))) )

    # 生成手工凭证
    def compute_account_voucher(self, cr, uid, po_codes, journal_id, line, context=None):
        voucher_obj = self.pool.get('account.voucher')
        voucher_data = {'partner_id': line.partner_id.id,
                        'account_id': line.account_id.id,
                        'date': date.today(),
                        'reference': po_codes if po_codes else False,
                        'amount': line.apply_amount,
                        'type': 'payment',
                        'company_id': line.partner_id.company_id.id,
                        'currency_id': line.currency_id.id,
                        # 付款方式默认不填，需要操作人员手动填写
                        'journal_id': journal_id,
                        'is_apply': True,
                        }
        return voucher_obj.create(cr, uid, voucher_data, context=context)

    # 生成预付款的手工凭证明细
    def compute_voucher_payment_line(self, cr, uid, voucher_id, voucher_ids, context=None):
        account_voucher_obj = self.pool.get('account.voucher')
        voucher_line_obj = self.pool.get('account.voucher.line')
        account_move_line_obj = self.pool.get('account.move.line')

        voucher_payment_line_ids = []
        # 根据凭证，找到符合条件的mvoe_line
        for account_voucher in account_voucher_obj.browse(cr, uid, voucher_ids, context=context):
            move_line_ids = account_move_line_obj.search(cr, uid, [('move_id', '=', account_voucher.move_id.id), ('account_id', '=', account_voucher.account_id.id)])
            for move_line in account_move_line_obj.browse(cr, uid, move_line_ids, context=context):
                if not move_line.reconcile_id and move_line.debit > 0:
                    # 生成预付款凭证行数据
                    voucher_payment_line_data = {'move_line_id': move_line.id,
                                                 'account_id': move_line.account_id.id,
                                                 'company_id': move_line.company_id.id,
                                                 'type': 'cr',
                                                 'date_original': date.today(),
                                                 'date_due': move_line.date_maturity,
                                                 'partner_id': move_line.partner_id,
                                                 'currency_id': False,
                                                 'amount_original': move_line.debit,
                                                 'amount_unreconciled': move_line.debit,
                                                 'voucher_id': voucher_id,
                                                 'reconcile': True,
                                                 'amount': move_line.debit,
                                                 }
                    voucher_payment_line_id = voucher_line_obj.create(cr, uid, voucher_payment_line_data, context=context)
                    voucher_payment_line_ids.append(voucher_payment_line_id)
        return voucher_payment_line_ids

    # 生成借方DR 贷方CR的手工凭证明细
    def compute_voucher_line(self, cr, uid, voucher_id, voucher_list, line, type, context=None):
        voucher_line_obj = self.pool.get('account.voucher.line')
        voucher_line_ids = []
        # 查找相应的会计凭证行数据
        cr.execute("""SELECT aml.id AS move_line_id,
	                         ai.partner_id AS partner_id,
	                         ai.currency_id AS currency_id,
	                         ai.company_id AS company_id,
	                         am.date AS date,
	                         aml.date_maturity AS date_maturity,
	                         aml.account_id AS account_id,
	                         ai.amount_total AS amount_total,
	                         ai.residual AS residual,
	                         ai.id as invoice_id
                      FROM account_invoice AS ai
                      INNER JOIN account_move AS am ON ai.move_id=am.id
                      INNER JOIN account_move_line AS aml ON am.id=aml.move_id AND aml.account_id=ai.account_id
                      WHERE aml.state ='valid' AND aml.reconcile_id IS NULL AND ai.id in (%s)
                    """ % (','.join(voucher_list)) )
        move_records = cr.dictfetchall()
        for move_record in move_records:

            voucher_line_data = {'move_line_id': move_record['move_line_id'],
                                 'account_id': move_record['account_id'],
                                 'company_id': move_record['company_id'],
                                 'type': type,
                                 'date_original': date.today(),
                                 'date_due': move_record['date_maturity'],
                                 'partner_id': move_record['partner_id'],
                                 'currency_id': move_record['currency_id'],
                                 'amount_original': move_record['amount_total'],
                                 'amount_unreconciled': move_record['residual'],
                                 'voucher_id': voucher_id,
                                 'reconcile': True,
                                 'amount': move_record['residual'],
                                 'invoice_id': move_record['invoice_id'],
                                 }
            voucher_line_id = voucher_line_obj.create(cr, uid, voucher_line_data, context=context)
            voucher_line_ids.append(voucher_line_id)
        return voucher_line_ids

    def fields_view_get(self, cr, uid, view_id=None, view_type='form',
                        context=None, toolbar=False, submenu=False):
        # 调整付款申请这里的2个弹出窗口,有错误提示时只显示一个弹窗
        if context is None:
            context = {}
        res = super(apply_payment_wizard, self).fields_view_get(
            cr, uid, view_id=view_id, view_type=view_type, context=context, toolbar=toolbar, submenu=False)
        if context.get('active_model', '') == 'apply.payment':
            active_ids = context.get('active_ids')
            error_ids = self.active_ids_check(cr, uid, active_ids, context=None)
            if error_ids:
                raise osv.except_osv(u'警告', u'部分发票:%s 还未记账,请记账之后再操作' % (error_ids))
        return res

    # 重载向导的default_get方法，初始化数据
    def default_get(self, cr, uid, fields, context=None):
        res = super(apply_payment_wizard, self).default_get(cr, uid, fields, context=context)
        active_ids = context.get('active_ids')

        if len(active_ids) > 1:
            operation = 'in %s' % (str(tuple(active_ids)))
        else:
            operation = '= %d' % (active_ids[0])
        # 找到采购分录与采购退款分录的数据库id
        account_journal_obj = self.pool.get('account.journal')
        purchase_journal_id = account_journal_obj.search(cr, uid, [('type', '=', 'purchase')], context=context)
        purchase_refund_journal_id = account_journal_obj.search(cr, uid, [('type', '=', 'purchase_refund')], context=context)

        # 将采购单、发票join,先子查询--按采购单来group by，主查询--再来按供应商group by
        cr.execute(''' SELECT foo.partner_id AS partner_id,
                             string_agg(CAST(foo.journal_id AS varchar),',') AS journal_ids,
                             string_agg(CAST(foo.po_id AS varchar),',') AS po_ids,
                             min(foo.currency_id) AS currency_id,
                             min(foo.account_id) AS account_id,
                             SUM(foo.advance_payment) AS advance_payment,
                             string_agg(dr_invoice_ids,',') AS dr_invoice_ids,
                             string_agg(cr_invoice_ids,',') AS cr_invoice_ids,
                             SUM(dr_invoice_amount) AS dr_invoice_amount,
                             SUM(cr_invoice_amount) AS cr_invoice_amount
                      FROM (SELECT po.id AS po_id,
                                   po.partner_id AS partner_id,
                                   ai.journal_id AS journal_id,
                                   --如果预付款已经申请过，才能进行叠加计算
                                   (CASE WHEN po.voucher_id IS NOT NULL
                                         THEN 0-po.advance_payment
                                         ELSE 0 END)AS advance_payment,
                                   (CASE WHEN ai.journal_id=%d THEN string_agg(CAST(ai.id AS varchar),',') ELSE '' END) AS dr_invoice_ids,
                                   (CASE WHEN ai.journal_id=%d THEN string_agg(CAST(ai.id AS varchar),',') ELSE '' END) AS cr_invoice_ids,
                                   min(ai.currency_id) AS currency_id,
                                   min(ai.account_id) AS account_id,
                                   (CASE WHEN ai.journal_id=3 THEN SUM(ai.residual) ELSE 0 END) AS dr_invoice_amount,
                                   (CASE WHEN ai.journal_id=5 THEN 0-SUM(ai.residual) ELSE 0 END) AS cr_invoice_amount
                            FROM purchase_order AS po
                            JOIN purchase_invoice_rel AS pir ON po.id=pir.purchase_id
                            JOIN account_invoice AS ai ON pir.invoice_id=ai.id
                            WHERE ai.id %s
                            GROUP BY  po.id, ai.journal_id) AS foo
                      GROUP BY foo.partner_id''' % (purchase_journal_id[0], purchase_refund_journal_id[0], operation))
        line_data = cr.dictfetchall()
        # 给申请金额字段赋值
        for line in line_data:
            line.update({'apply_amount': line.get('dr_invoice_amount') + line.get('cr_invoice_amount') + line.get('advance_payment')})

        res.update({'wizard_line': line_data})
        return res

    # 检查不符合条件（还为open）的发票
    def active_ids_check(self, cr, uid, active_ids, context=None):
        res = self.pool.get("account.invoice").search(cr, uid, ['&', ('id', 'in', active_ids),
                                                                ('state', 'in', ['draft', 'cancel', 'proforma2'])],
                                                      context=context)
        return res

    _columns = {
        'wizard_line': fields.one2many('apply.payment.wizard.line', 'wizard_id', string=u'向导明细'),
    }


class apply_payment_wizard_line(osv.osv_memory):
    _name = 'apply.payment.wizard.line'
    _description = u'申请付款向导明细'

    _columns = {
        'wizard_id': fields.many2one('apply.payment.wizard', string=u'申请付款向导ID'),
        'po_ids': fields.char(string=u'采购单'),
        'is_advance_payment': fields.boolean(string=u'预付款'),
        'account_id': fields.many2one('account.account', string=u'会计科目ID'),
        'partner_id': fields.many2one('res.partner', string=u'供应商'),
        'currency_id': fields.many2one('res.currency', string=u'货币'),
        'advance_payment': fields.float(string=u'预付款金额'),
        'journal_ids': fields.char(string='会计分录'),
        'invoice_amount': fields.float(string=u'发票金额'),
        'dr_invoice_ids': fields.char(string='借方发票ID'),
        'cr_invoice_ids': fields.char(string='贷方发票ID'),
        'dr_invoice_amount': fields.float(string='借方金额'),
        'cr_invoice_amount': fields.float(string='贷方金额'),
        'apply_amount': fields.float(string='申请金额')
    }


class stock_invoice_onshipping(osv.osv_memory):
    _inherit = "stock.invoice.onshipping"
    _description = "Stock Invoice Onshipping"

    # 继承“创建发票”向导的onchange_journal_id方法，使   出入库单  生成对应的“采购订单红字发票”和“采购订单发票”
    def onchange_journal_id(self, cr, uid, ids, journal_id, context=None):
        value = super(stock_invoice_onshipping, self).onchange_journal_id(cr, uid, ids, journal_id)
        domain = {}
        active_ids = context.get('active_ids')
        picking = self.pool['stock.picking'].browse(cr, uid, active_ids, context=context)
        origin_before = ''
        for pick in picking:
            usage = pick.move_lines[0].location_id.usage if type == 'incoming' else pick.move_lines[0].location_dest_id.usage
            journal_types = JOURNAL_TYPE_MAP.get((type, usage), ['purchase', 'purchase_refund'])
            domain['journal_id'] = [('type', 'in', journal_types)]
            # 判断是否生成不同类型的采购订单相关发票
            if origin_before:
                if origin_before[0] != pick.origin[0]:
                    raise osv.except_osv(u'警告', u'您不能同时生成不同类型的采购订单相关发票%s,%s！' % (origin_before, pick.origin))
            origin_before = pick.origin

            if journal_id:
                journal = self.pool['account.journal'].browse(cr, uid, journal_id, context=context)
                value['journal_type'] = journal.type
                # 如果分拣单为入库单，而发票不是采购订单发票，则警告
                if pick.picking_type_id.default_location_src_id.usage == 'supplier' and journal.type != 'purchase':
                        raise osv.except_osv(u'警告', u'%s为采购订单（入库），只能生成采购订单发票' % (pick.origin))
                if pick.picking_type_id.default_location_dest_id.usage == 'supplier' and journal.type != 'purchase_refund':
                        raise osv.except_osv(u'警告', u'%s为采购订单的退货发票（出库），只能生成采购订单红字发票' % (pick.origin))

        return {'value': value, 'domain': domain}

    # 继承create_invoice方法，为发票行产品quantity按需求赋值
    def create_invoice(self, cr, uid, ids, context=None):
        res = super(stock_invoice_onshipping, self).create_invoice(cr, uid, ids, context=context)
        stock_picking_obj = self.pool.get('stock.picking')
        stock_move_obj = self.pool.get('stock.move')
        account_invoice_obj = self.pool.get('account.invoice')
        account_invoice_line_obj = self.pool.get('account.invoice.line')

        active_ids = context.get('active_ids', [])
        account_invoice_id = account_invoice_obj.search(cr, uid, [('id', 'in', res)], context=context)
        account_line_id = account_invoice_line_obj.search(cr, uid, [('invoice_id', 'in', account_invoice_id)], context=context)

        for picking_id in active_ids:
            picking = stock_picking_obj.browse(cr, uid, picking_id, context=context)
            # 如果当前分拣单为退货单，跳过
            if picking.origin_picking_id:
                continue
            # 取得对应的所有退货单
            reject_picking_ids = stock_picking_obj.search(cr, uid, [('origin_picking_id', '=', picking_id)], context=context)
            products_number = {}
            if reject_picking_ids:
                # 遍历当前订单对应的每一个退货单，并按产品不同分别求退货产品数量的和
                for reject_picking in stock_picking_obj.browse(cr, uid, reject_picking_ids, context=context):
                    for move_line in reject_picking.move_lines:
                        if not products_number.get(move_line.product_id):
                            products_number.update({
                                move_line.product_id: move_line.product_uom_qty
                            })
                        elif products_number.get(move_line.product_id):
                            products_number[move_line.product_id] += move_line.product_uom_qty
                    # 把对应的退货单行和退货单的发票状态设置为“不开票”状态
                        stock_move_obj .write(cr, uid, move_line.id, {'invoice_state': 'none'}, context=context)
                    stock_picking_obj.write(cr, uid, reject_picking.id, {'invoice_state': 'none'}, context=context)

                # 如果有退货产品的数量,处理res中的所有发票行，把发票行的产品数量减去products_number中的退货产品数量
                quantity = -1
                if products_number.get(move_line.product_id):
                    for account in account_invoice_line_obj.browse(cr, uid, account_line_id, context=context):
                        for product_id_key in products_number:
                            if account.product_id == product_id_key:
                                quantity = account.quantity - products_number[product_id_key]
                        # 把最后需要开票的产品数量写回发票行
                        if quantity != -1:
                            account_invoice_line_obj.write(cr, uid, account.id, {'quantity': quantity}, context=context)
                # 更新发票“检查合计数”字段的值
                if quantity != -1:
                    account_invoices = account_invoice_obj.search(cr, uid, [('invoice_line', 'in', account_line_id)], context=context)
                    for account_invoice in account_invoice_obj.browse(cr, uid, account_invoices, context=context):
                        check_total = account_invoice.amount_total
                        account_invoice_obj.write(cr, uid, account_invoice.id, {'check_total': check_total})

        return res

class account_bank_statement_line(osv.osv):
    _inherit = 'account.bank.statement.line'


    def process_reconciliation(self, cr, uid, id, mv_line_dicts, context=None):
        """ Creates a move line for each item of mv_line_dicts and for the statement line. Reconcile a new move line with its counterpart_move_line_id if specified. Finally, mark the statement line as reconciled by putting the newly created move id in the column journal_entry_id.

            :param int id: id of the bank statement line
            :param list of dicts mv_line_dicts: move lines to create. If counterpart_move_line_id is specified, reconcile with it
        """
        if context is None:
            context = {}
        st_line = self.browse(cr, uid, id, context=context)
        company_currency = st_line.journal_id.company_id.currency_id
        statement_currency = st_line.journal_id.currency or company_currency
        bs_obj = self.pool.get('account.bank.statement')
        am_obj = self.pool.get('account.move')
        aml_obj = self.pool.get('account.move.line')
        currency_obj = self.pool.get('res.currency')

        # Checks
        if st_line.journal_entry_id.id:
            raise osv.except_osv(_('Error!'), _('The bank statement line was already reconciled.'))
        for mv_line_dict in mv_line_dicts:
            for field in ['debit', 'credit', 'amount_currency']:
                if field not in mv_line_dict:
                    mv_line_dict[field] = 0.0
            if mv_line_dict.get('counterpart_move_line_id'):
                mv_line = aml_obj.browse(cr, uid, mv_line_dict.get('counterpart_move_line_id'), context=context)
                if mv_line.reconcile_id:
                    raise osv.except_osv(_('Error!'), _('A selected move line was already reconciled.'))

        # Create the move
        move_name = (st_line.statement_id.name or st_line.name) + "/" + str(st_line.sequence)
        move_vals = bs_obj._prepare_move(cr, uid, st_line, move_name, context=context)
        move_id = am_obj.create(cr, uid, move_vals, context=context)

        # Create the move line for the statement line
        if st_line.statement_id.currency.id != company_currency.id:
            if st_line.currency_id == company_currency:
                amount = st_line.amount_currency
            else:
                ctx = context.copy()
                ctx['date'] = st_line.date
                amount = currency_obj.compute(cr, uid, st_line.statement_id.currency.id, company_currency.id, st_line.amount, context=ctx)
        else:
            amount = st_line.amount
        bank_st_move_vals = bs_obj._prepare_bank_move_line(cr, uid, st_line, move_id, amount, company_currency.id, context=context)
        aml_obj.create(cr, uid, bank_st_move_vals, context=context)
        # Complete the dicts
        st_line_currency = st_line.currency_id or statement_currency
        st_line_currency_rate = st_line.currency_id and (st_line.amount_currency / st_line.amount) or False
        to_create = []
        for mv_line_dict in mv_line_dicts:
            if mv_line_dict.get('is_tax_line'):
                continue
            mv_line_dict['ref'] = move_name
            mv_line_dict['move_id'] = move_id
            mv_line_dict['period_id'] = st_line.statement_id.period_id.id
            mv_line_dict['journal_id'] = st_line.journal_id.id
            mv_line_dict['company_id'] = st_line.company_id.id
            mv_line_dict['statement_id'] = st_line.statement_id.id
            if mv_line_dict.get('counterpart_move_line_id'):
                mv_line = aml_obj.browse(cr, uid, mv_line_dict['counterpart_move_line_id'], context=context)
                mv_line_dict['partner_id'] = mv_line.partner_id.id or st_line.partner_id.id
                mv_line_dict['account_id'] = mv_line.account_id.id
            if st_line_currency.id != company_currency.id:
                ctx = context.copy()
                ctx['date'] = st_line.date
                mv_line_dict['amount_currency'] = mv_line_dict['debit'] - mv_line_dict['credit']
                mv_line_dict['currency_id'] = st_line_currency.id
                if st_line.currency_id and statement_currency.id == company_currency.id and st_line_currency_rate:
                    debit_at_current_rate = self.pool.get('res.currency').round(cr, uid, company_currency, mv_line_dict['debit'] / st_line_currency_rate)
                    credit_at_current_rate = self.pool.get('res.currency').round(cr, uid, company_currency, mv_line_dict['credit'] / st_line_currency_rate)
                elif st_line.currency_id and st_line_currency_rate:
                    debit_at_current_rate = currency_obj.compute(cr, uid, statement_currency.id, company_currency.id, mv_line_dict['debit'] / st_line_currency_rate, context=ctx)
                    credit_at_current_rate = currency_obj.compute(cr, uid, statement_currency.id, company_currency.id, mv_line_dict['credit'] / st_line_currency_rate, context=ctx)
                else:
                    debit_at_current_rate = currency_obj.compute(cr, uid, st_line_currency.id, company_currency.id, mv_line_dict['debit'], context=ctx)
                    credit_at_current_rate = currency_obj.compute(cr, uid, st_line_currency.id, company_currency.id, mv_line_dict['credit'], context=ctx)
                if mv_line_dict.get('counterpart_move_line_id'):
                    #post an account line that use the same currency rate than the counterpart (to balance the account) and post the difference in another line
                    ctx['date'] = mv_line.date
                    if mv_line.currency_id.id == mv_line_dict['currency_id'] \
                            and float_is_zero(abs(mv_line.amount_currency) - abs(mv_line_dict['amount_currency']), precision_rounding=mv_line.currency_id.rounding):
                        debit_at_old_rate = mv_line.credit
                        credit_at_old_rate = mv_line.debit
                    else:
                        debit_at_old_rate = currency_obj.compute(cr, uid, st_line_currency.id, company_currency.id, mv_line_dict['debit'], context=ctx)
                        credit_at_old_rate = currency_obj.compute(cr, uid, st_line_currency.id, company_currency.id, mv_line_dict['credit'], context=ctx)
                    mv_line_dict['credit'] = credit_at_old_rate
                    mv_line_dict['debit'] = debit_at_old_rate
                    if debit_at_old_rate - debit_at_current_rate:
                        currency_diff = debit_at_current_rate - debit_at_old_rate
                        to_create.append(self.get_currency_rate_line(cr, uid, st_line, -currency_diff, move_id, context=context))
                    if credit_at_old_rate - credit_at_current_rate:
                        currency_diff = credit_at_current_rate - credit_at_old_rate
                        to_create.append(self.get_currency_rate_line(cr, uid, st_line, currency_diff, move_id, context=context))
                    if mv_line.currency_id and mv_line_dict['currency_id'] == mv_line.currency_id.id:
                        amount_unreconciled = mv_line.amount_residual_currency
                    else:
                        amount_unreconciled = currency_obj.compute(cr, uid, company_currency.id, mv_line_dict['currency_id'] , mv_line.amount_residual, context=ctx)
                    if float_is_zero(mv_line_dict['amount_currency'] + amount_unreconciled, precision_rounding=mv_line.currency_id.rounding):
                        amount = mv_line_dict['debit'] or mv_line_dict['credit']
                        sign = -1 if mv_line_dict['debit'] else 1
                        currency_rate_difference = sign * (mv_line.amount_residual - amount)
                        if not company_currency.is_zero(currency_rate_difference):
                            exchange_lines = self._get_exchange_lines(cr, uid, st_line, mv_line, currency_rate_difference, mv_line_dict['currency_id'], move_id, context=context)
                            for exchange_line in exchange_lines:
                                to_create.append(exchange_line)

                else:
                    mv_line_dict['debit'] = debit_at_current_rate
                    mv_line_dict['credit'] = credit_at_current_rate
            elif statement_currency.id != company_currency.id:
                #statement is in foreign currency but the transaction is in company currency
                prorata_factor = (mv_line_dict['debit'] - mv_line_dict['credit']) / st_line.amount_currency
                mv_line_dict['amount_currency'] = prorata_factor * st_line.amount
            to_create.append(mv_line_dict)
        # If the reconciliation is performed in another currency than the company currency, the amounts are converted to get the right debit/credit.
        # If there is more than 1 debit and 1 credit, this can induce a rounding error, which we put in the foreign exchane gain/loss account.
        if st_line_currency.id != company_currency.id:
            diff_amount = bank_st_move_vals['debit'] - bank_st_move_vals['credit'] \
                + sum(aml['debit'] for aml in to_create) - sum(aml['credit'] for aml in to_create)
            if not company_currency.is_zero(diff_amount):
                diff_aml = self.get_currency_rate_line(cr, uid, st_line, diff_amount, move_id, context=context)
                diff_aml['name'] = _('Rounding error from currency conversion')
                to_create.append(diff_aml)
        # Create move lines
        move_line_pairs_to_reconcile = []
        new_to_create = {}
        for mv_line_dict in to_create:
            if mv_line_dict.get('account_id') in new_to_create:
                current_move_line = new_to_create.get(mv_line_dict.get('account_id'))
                current_move_line['debit'] += mv_line_dict['debit']
                current_move_line['credit'] += mv_line_dict['credit']
                if mv_line_dict.get('counterpart_move_line_id', False) and\
                    mv_line_dict['counterpart_move_line_id'] not in current_move_line['counterpart_move_line_id']:
                    current_move_line['counterpart_move_line_id'].append(mv_line_dict.get('counterpart_move_line_id'))
            else:
                if mv_line_dict.get('counterpart_move_line_id', False):
                    mv_line_dict['counterpart_move_line_id'] = [mv_line_dict['counterpart_move_line_id']]
                new_to_create.update({mv_line_dict.get('account_id'): mv_line_dict})
        
        for account_id, mv_line_dict in new_to_create.iteritems():
            counterpart_move_line_id = None # NB : this attribute is irrelevant for aml_obj.create() and needs to be removed from the dict
            if mv_line_dict.get('counterpart_move_line_id'):
                counterpart_move_line_id = mv_line_dict['counterpart_move_line_id']
                del mv_line_dict['counterpart_move_line_id']
            new_aml_id = aml_obj.create(cr, uid, mv_line_dict, context=context)
            if counterpart_move_line_id != None:
                counterpart_move_line_id.append(new_aml_id)
                move_line_pairs_to_reconcile.append([counterpart_move_line_id, mv_line_dict.get('account_id'),
                                                     mv_line_dict.get('period_id'), mv_line_dict.get('journal_id')])
        # Reconcile
        for pair in move_line_pairs_to_reconcile:
            aml_obj.res = aml_obj.reconcile(cr, uid, pair[0], 'manual', pair[1],
                                            pair[2], pair[3], context=context)
        # Mark the statement line as reconciled
        self.write(cr, uid, id, {'journal_entry_id': move_id}, context=context)

    def cancel(self, cr, uid, ids, context=None):
        account_move_obj = self.pool.get('account.move')
        move_ids = []
        for line in self.browse(cr, uid, ids, context=context):
            if line.journal_entry_id:
                move_ids.append(line.journal_entry_id.id)
                for aml in line.journal_entry_id.line_id:
                    if aml.reconcile_id:
                        move_lines = [l.id for l in aml.reconcile_id.line_id]
                        move_lines.remove(aml.id)
                        self.pool.get('account.move.reconcile').unlink(cr, uid, [aml.reconcile_id.id], context=context)
        if move_ids:
            account_move_obj.button_cancel(cr, uid, move_ids, context=context)
            account_move_obj.unlink(cr, uid, move_ids, context)


class dftg_stock_invoice_onshipping(osv.osv_memory):
    _inherit = "stock.invoice.onshipping"

    _defaults = {
        'group': True
    }

class account_journal(osv.osv):
    _inherit = 'account.journal'
    _columns = {
        'active': fields.boolean('激活')
    }
    _defaults = {
        'active': True
    }
    
class dftg_stock_invoice_onshipping(osv.osv_memory):
    _inherit = 'stock.invoice.onshipping'
    
    def fields_view_get(self, cr, uid, view_id=None, view_type='form',
                        context=None, toolbar=False, submenu=False):
        if context is None:
            context = {}
        res = super(dftg_stock_invoice_onshipping, self).fields_view_get(
            cr, uid, view_id=view_id, view_type=view_type, context=context, toolbar=toolbar, submenu=False)
        if context.get('active_model', '') == 'stock.picking':
            active_ids = context.get('active_ids')
            if len(list(set([pikcing.picking_type_code for pikcing in 
            self.pool.get('stock.picking').browse(cr, uid, context.get('active_ids'), context=context)]))) != 1:
                raise osv.except_osv(u'警告', u'采购退货发票需要单独开!')
        return res
    
